{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## _*FermionicOperator and qubit mapping*_\n",
    "\n",
    "When we compute a FermionicOperator in Qiskit Chemistry it needs to be converted to a qubit operator to run on the simulator or real device. The FermionicOperator is built from electronn integrals where electrons behave anti-symmetrically under swap. qubits however do not exhibit this behavior and hence a mapping is needed to ensure that this is accounted for.\n",
    "\n",
    "Here we have the jordan wigner mapping, the bravyi-kitaev mapping and a parity.\n",
    "\n",
    "This notebook has been written to use the PYQUANTE chemistry driver. See the PYQUANTE chemistry driver readme if you need to install the external PyQuante2 library that this driver requires."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "from qiskit import BasicAer\n",
    "\n",
    "from qiskit.aqua import QuantumInstance, aqua_globals\n",
    "from qiskit.aqua.algorithms import VQE, NumPyMinimumEigensolver\n",
    "from qiskit.aqua.components.optimizers import L_BFGS_B\n",
    "from qiskit.circuit.library import TwoLocal\n",
    "\n",
    "from qiskit.chemistry import FermionicOperator\n",
    "from qiskit.chemistry.drivers import PyQuanteDriver, UnitsType, BasisType\n",
    "\n",
    "aqua_globals.random_seed = 50"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# using driver to get fermionic Hamiltonian\n",
    "# PyQuante example\n",
    "driver = PyQuanteDriver(atoms='H .0 .0 .0; H .0 .0 0.735', units=UnitsType.ANGSTROM,\n",
    "                        charge=0, multiplicity=1, basis=BasisType.BSTO3G)\n",
    "molecule = driver.run()\n",
    "h1 = molecule.one_body_integrals\n",
    "h2 = molecule.two_body_integrals"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# convert from fermionic hamiltonian to qubit hamiltonian\n",
    "ferOp = FermionicOperator(h1=h1, h2=h2)\n",
    "qubitOp_jw = ferOp.mapping(map_type='JORDAN_WIGNER', threshold=0.00000001)\n",
    "qubitOp_pa = ferOp.mapping(map_type='PARITY', threshold=0.00000001)\n",
    "qubitOp_bk = ferOp.mapping(map_type='BRAVYI_KITAEV', threshold=0.00000001)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " --- jordan wigner ---\n",
      "Representation: paulis, qubits: 4, size: 15\n",
      "The exact ground state energy using jordan wigner mapping is: -1.857275076637873\n",
      "\n",
      " --- parity ---\n",
      "Representation: paulis, qubits: 4, size: 15\n",
      "The exact ground state energy using parity mapping is: -1.857275076637872\n",
      "\n",
      " --- bravyi-kitaev ---\n",
      "Representation: paulis, qubits: 4, size: 15\n",
      "The exact ground state energy using bravyi-kitaev mapping is: -1.8572750766378743\n"
     ]
    }
   ],
   "source": [
    "# print out qubit hamiltonian in Pauli terms and exact solution\n",
    "qubit_ops = [(qubitOp_jw, 'jordan wigner'),\n",
    "            (qubitOp_pa, 'parity'),\n",
    "            (qubitOp_bk, 'bravyi-kitaev')]\n",
    "\n",
    "for qubit_op, name in qubit_ops:\n",
    "    qubit_op.chop(10**-10)\n",
    "\n",
    "    print(\"\\n --- {} ---\".format(name))\n",
    "    print(qubit_op)\n",
    "\n",
    "    # Using exact eigensolver to get the smallest eigenvalue\n",
    "    exact_eigensolver = NumPyMinimumEigensolver(qubit_op)\n",
    "    ret = exact_eigensolver.run()\n",
    "    print('The exact ground state energy using {} mapping is: {}'.format(name, ret.eigenvalue.real))    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we run on quantum backend, in this case a simulator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ground state energy using jordan wigner: -1.85727476869041\n",
      "Ground state energy using parity: -1.857274320675\n",
      "Ground state energy using bravyi-kitaev: -1.85726132279563\n"
     ]
    }
   ],
   "source": [
    "for qubit_op, name in qubit_ops:\n",
    "    # setup VQE \n",
    "    # setup optimizer, use L_BFGS_B optimizer for example\n",
    "    lbfgs = L_BFGS_B(maxfun=1000, factr=10, iprint=10)\n",
    "\n",
    "    # setup variational form generator (generate trial circuits for VQE)\n",
    "    var_form = TwoLocal(qubit_op.num_qubits, 'ry', 'cz', reps=5, entanglement='full')\n",
    "\n",
    "    # setup VQE with operator, variational form, and optimizer\n",
    "    vqe_algorithm = VQE(qubit_op, var_form, lbfgs)\n",
    "\n",
    "    backend = BasicAer.get_backend('statevector_simulator')\n",
    "    quantum_instance = QuantumInstance(backend)\n",
    "\n",
    "    results = vqe_algorithm.run(quantum_instance)\n",
    "\n",
    "    print(\"Ground state energy using {}: {}\".format(name, results.eigenvalue.real))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
